﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using STSdb.Data;
using STSdb.Persist;
using STSdb.General.Collections;

namespace STSdb.MTL
{
    /// <summary>
    /// Internal wrapper for XTable, to which must WRITE
    /// Not fully implemented, by some reasons. It'S used only by Transaction Coordinator Get_Table_Write.
    /// Normally wraps real XTable and makes extra activity, like Transactions logging and calculating quantity of inserts, for running
    /// Compaction check.
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TRecord"></typeparam>
    public class XTableMTL<TKey, TRecord> : ITransaction, IEnumerable<Row<TKey, TRecord>>
    {
        XTable<TKey, TRecord> _xtable=null;

        /// <summary>
        /// _originalUserTableName
        /// </summary>
        string _tableName = String.Empty;
        TransactionsCoordinator _transactionCoordinator = null;

        public XTableMTL(XTable<TKey, TRecord> xtable, string tableName, TransactionsCoordinator transactionCoordinator)
        {
            this._xtable = xtable;
            this._tableName = tableName;
            this._transactionCoordinator = transactionCoordinator;
        }

        
        #region Manage

       

        /// <summary>
        /// Gets or sets the record associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the record to get or set.</param>
        /// <returns>The record associated with the specified key. If the specified key is not found, a get operation throws a KeyNotFoundException, and a set operation creates a new record with the specified key.</returns>
        /// <exception cref="KeyNotFoundException">The property is retrieved and key does not exist in the collection.</exception>
        public TRecord this[TKey key]
        {
            get
            {
                return this._xtable[key];                
            }
            set
            {
                //Sending statistic that table intended to be modified
                this._transactionCoordinator.TablesCoordinator.GetTable<TKey, TRecord>(this._tableName).IntentToModifyRecord(false);

                this._xtable[key] = value;               
            }
        }

        /// <summary>
        /// Deletes a record with the specified key.
        /// </summary>
        /// <param name="key">The key of the record to delete.</param>
        public void Delete(TKey key)
        {
            //Sending statistic that table intended to be modified
            this._transactionCoordinator.TablesCoordinator.GetTable<TKey, TRecord>(this._tableName).IntentToModifyRecord(false);

            this._xtable.Delete(key);
        }

        /// <summary>
        /// Deletes all records with keys in range [firstKey, lastKey].
        /// </summary>
        /// <param name="firstKey">The first key of the range of records to delete.</param>
        /// <param name="lastKey">The last key of the range of records to delete.</param>
        /// <exception cref="ArgumentException">If firstKey is greater than lastKey</exception>
        public void Delete(TKey firstKey, TKey lastKey)
        {
            //Sending statistic that table intended to be modified
            this._transactionCoordinator.TablesCoordinator.GetTable<TKey, TRecord>(this._tableName).IntentToModifyRecord(true);

            this._xtable.Delete(firstKey,lastKey);
        }

        /// <summary>
        /// Deletes all records from the table.
        /// </summary>
        public void Clear()
        {
            //Sending statistic that table intended to be modified
            this._transactionCoordinator.TablesCoordinator.GetTable<TKey, TRecord>(this._tableName).IntentToModifyRecord(true);

            this._xtable.Clear();
        }

        /// <summary>
        /// Commit changes and close the table.
        /// </summary>
        public void Close()
        {
            this._xtable.Close();
        }

        /// <summary>
        /// Adds all rows from table. On key conflict rows from specified table replace avaiable rows.
        /// </summary>
        /// <param name="table"></param>
        public void Merge(XTable<TKey, TRecord> table)
        {
            //Sending statistic that table intended to be modified
            this._transactionCoordinator.TablesCoordinator.GetTable<TKey, TRecord>(this._tableName).IntentToModifyRecord(true);

            this._xtable.Merge(table);
        }

        public void Change(TKey key, TKey newKey)
        {
            //Sending statistic that table intended to be modified
            this._transactionCoordinator.TablesCoordinator.GetTable<TKey, TRecord>(this._tableName).IntentToModifyRecord(false);

            this._xtable.Change(key, newKey);
        }

        public void Swap(TKey key1, TKey key2)
        {
            //Sending statistic that table intended to be modified
            this._transactionCoordinator.TablesCoordinator.GetTable<TKey, TRecord>(this._tableName).IntentToModifyRecord(false);

            this._xtable.Swap(key1,key2);
        }

        #endregion

        #region Find Methods

        /// <summary>
        /// Gets the record associated with the specified key.
        /// </summary>
        /// <param name="key">The key of the record to get.</param>
        /// <param name="record">When this method returns, contains the record associated with the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.</param>
        /// <returns>true if the XTable<TKey, TValue> contains a record with the specified key; otherwise, false.</returns>
        public bool TryGet(TKey key, out TRecord record)
        {
            return this._xtable.TryGet(key, out record);
        }

        public TRecord TryGetDefault(TKey key, TRecord defaultRecord)
        {
            return this._xtable.TryGetDefault(key,defaultRecord);
        }

        /// <summary>
        /// Searches for a row with the specified key.
        /// </summary>
        /// <param name="key">The key of the row to get.</param>
        /// <returns>Row with the specified key or null, if a row with such key does not exists.</returns>
        public TRecord Find(TKey key)
        {            
            return this._xtable.Find(key);
        }

        /// <summary>
        /// Determines whether the table contains a record with the specified key.
        /// </summary>
        /// <param name="key">The key to locate in the table.</param>
        /// <returns>True, if the table contains an element with the specified key; otherwise, false.</returns>
        public bool Exists(TKey key)
        {
            return this._xtable.Exists(key);
        }

        internal Row<TKey, TRecord> FindNext(byte[] index)
        {
            return this._xtable.FindNext(index);
        }

        /// <summary>
        /// Searches for a row with key greater than or equals to the specified key.
        /// </summary>
        /// <param name="key">The key of the row to get.</param>
        /// <returns>If the key is found it returns the record associated with it. If the key
        /// doesn't exist it returns the first record with a key greater than the
        /// specified one. If there are no records with keys greater or equal to
        /// the specified one it returns null.</returns>
        public Row<TKey, TRecord> FindNext(TKey key)
        {
            return this._xtable.FindNext(key);
        }

        internal Row<TKey, TRecord> FindPrev(byte[] index)
        {
            return this._xtable.FindPrev(index);
        }

        /// <summary>
        /// Searches for a row with key less than or equals to the specified key.
        /// </summary>
        /// <param name="key">The key of the row to get.</param>
        /// <returns>If the key is found it returns the record associated with it. If the key
        /// doesn't exist it returns the first record with a key less than the
        /// specified one. If there are no records with keys less or equal to
        /// the specified one it returns null.</returns>
        public Row<TKey, TRecord> FindPrev(TKey key)
        {
            return this._xtable.FindPrev(key);
        }

        /// <summary>
        /// Searches for a row with key greater than the specified.
        /// </summary>
        /// <param name="key">The key of the row to get.</param>
        /// <returns>The first record with a key greater than the specified one.
        /// If there are no records with keys greater than the specified one it returns null.</returns>
        public Row<TKey, TRecord> FindAfter(TKey key)
        {
            return this._xtable.FindAfter(key);
        }

        /// <summary>
        /// Searches for a row with key less than the specified.
        /// </summary>
        /// <param name="key">The key of the row to get.</param>
        /// <returns>The first record with a key less than the specified one.
        /// If there are no records with keys less than the specified one it returns null.</returns>
        public Row<TKey, TRecord> FindBefore(TKey key)
        {

            return this._xtable.FindBefore(key);
        }

        #endregion

        #region Enumerators

        public IEnumerable<Row<TKey, TRecord>> Forward(byte[] firstIndex, byte[] lastIndex)
        {
            return this._xtable.Forward(firstIndex, lastIndex);        
        }

        public IEnumerable<Row<TKey, TRecord>> Forward(byte[] firstIndex)
        {
            return this._xtable.Forward(firstIndex);   
        }

        public IEnumerable<Row<TKey, TRecord>> Backward(byte[] lastIndex, byte[] firstIndex)
        {
            return this._xtable.Backward(firstIndex, lastIndex);
        }


        public IEnumerable<Row<TKey, TRecord>> Backward(byte[] lastIndex)
        {
            return this._xtable.Backward(lastIndex);
        }

        /// <summary>
        /// Enumerates all records with keys in range [firstKey, lastKey] in ascending order.
        /// </summary>
        /// <param name="firstKey">left border of the range.</param>
        /// <param name="lastKey">right border of the range.</param>
        /// <returns>Row enumerator.</returns>
        public IEnumerable<Row<TKey, TRecord>> Forward(TKey firstKey, TKey lastKey)
        {
            return this._xtable.Forward(firstKey, lastKey);
        }

        /// <summary>
        /// Enumerates all records with keys in range [firstKey, lastKey] in descending order.
        /// </summary>
        /// <param name="lastKey">right border of the range.</param>
        /// <param name="firstKey">left border of the range.</param>
        /// <returns>Row enumerator.</returns>
        public IEnumerable<Row<TKey, TRecord>> Backward(TKey lastKey, TKey firstKey)
        {
            return this._xtable.Backward(lastKey, firstKey);
        }

        /// <summary>
        /// Enumerates all records with keys greater than or equal to the specified key in ascending order.
        /// </summary>
        /// <param name="firstKey">enumeration initial key.</param>
        /// <returns>Row enumerator.</returns>
        public IEnumerable<Row<TKey, TRecord>> Forward(TKey firstKey)
        {
            return this._xtable.Forward(firstKey);
        }

        /// <summary>
        /// Enumerates all records with keys less than or equal to the specified key in descending order.
        /// </summary>
        /// <param name="lastKey">enumeration initial key.</param>
        /// <returns>Row enumerator.</returns>
        public IEnumerable<Row<TKey, TRecord>> Backward(TKey lastKey)
        {
            return this._xtable.Backward(lastKey);
        }

        /// <summary>
        /// Enumerates all records in descending order.
        /// </summary>
        /// <returns>Row enumerator.</returns>
        public IEnumerable<Row<TKey, TRecord>> Backward()
        {
            return this._xtable.Backward();
        }

        /// <summary>
        /// Enumerates all records in ascending order.
        /// </summary>
        /// <returns>Row enumerator.</returns>
        public IEnumerable<Row<TKey, TRecord>> Forward()
        {
            return this._xtable.Forward();
        }

        //public IEnumerable<Row<TKey, TRecord>> ForwardGroup(byte[] index)
        //{
        //    if (index == null)
        //        throw new ArgumentNullException("index");

        //    foreach (var row in Scheme.Forward((uint)index.Length))
        //    {
        //        uint depth = row.Key;
        //        FTable<TKey, TRecord> table = Retrieve(depth, false);

        //        foreach (var innerRow in table.ForwardGroup(index))
        //            yield return innerRow;
        //    }
        //}

        //public IEnumerable<Row<TKey, TRecord>> BackwardGroup(byte[] index)
        //{
        //    if (index == null)
        //        throw new ArgumentNullException("index");

        //    foreach (var row in Scheme.Backward((uint)index.Length))
        //    {
        //        uint depth = row.Key;
        //        FTable<TKey, TRecord> table = Retrieve(depth, false);

        //        foreach (var innerRow in table.BackwardGroup(index))
        //            yield return innerRow;
        //    }
        //}

        //public IEnumerable<Row<TKey, TRecord>> ForwardGroup(TKey key)
        //{
        //    byte[] index = KeyMap.Direct(key);
        //    return ForwardGroup(index);
        //}

        //public IEnumerable<Row<TKey, TRecord>> BackwardGroup(TKey key)
        //{
        //    byte[] index = KeyMap.Direct(key);
        //    return Backward(index, index);
        //}

        public IEnumerator<Row<TKey, TRecord>> GetEnumerator()
        {
            return this._xtable.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this._xtable.GetEnumerator();
        }

        #endregion

        /// <summary>
        /// Gets the number of rows actually contained in the table.
        /// </summary>
        public ulong Count {
            get{
                return this._xtable.Count;
            }
        }

        ///// <summary>
        ///// Gets the version of the table.
        ///// </summary>
        //public ushort Version { get { return this._xtable.Version; } }

        /// <summary>
        /// Make an isolated copy (snapshot) of the table. 
        /// </summary>
        /// <returns>Snapshot of the table.</returns>
        public XTable<TKey, TRecord> Snapshot()
        {
            return this._xtable.Snapshot();
        }

        //public IBinaryPersist<IIndexer<TRecord>> RecordPersist
        //{
        //    get { return this._xtable.RecordPersist; }
        //    set
        //    {
        //        this._xtable.RecordPersist = value;
        //    }
        //}


        public IEnumerable<JournalItem> Journal()
        {
            return this._xtable.Journal();
        }

        public void Commit()
        {
            this._xtable.Commit();
        }

        public void Rollback()
        {
            this._xtable.Rollback();
        }

        
    }
}
